/* ****************************************************************************
 *
 * Copyright (c) Microsoft Corporation. 
 *
 * This source code is subject to terms and conditions of the Microsoft Public License. A 
 * copy of the license can be found in the License.html file at the root of this distribution. If 
 * you cannot locate the  Microsoft Public License, please send an email to 
 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
 * by the terms of the Microsoft Public License.
 *
 * You must not remove this notice, or any other, from this software.
 *
 * THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
 * PARTICULAR PURPOSE.
 * ***************************************************************************/


#include "stdafx.h"
#include <pgl/core/PGLText.h>
#include <pgl/core/PGLTextPropPage.h>
#include <pgl/core/PGLArchive.h>
#include "../IGfx/GDILeak.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
PGL_IMPLEMENT_DYNCREATE(CPGLText,CPGLObject);
PGL_IMPLEMENT_SCRIPTING(Text);


//! Save settings
void CPGLText::SerializeXML(CPGLArchive& ar)
{
	using namespace gfxinterface;
	static const TCHAR szTag[] = _T("CPGLText");
	CString str;

	markup::CMarkupArchive& xml = *((markup::CMarkupArchive*)ar.GetMarkup());
	if(ar.IsStoring())
	{
		xml.AddChildElem(szTag);
		xml.AddChildAttrib(_T("Name"),GetName());
		xml.IntoElem();

		CPGLObject::SerializeXML(ar);

		xml.AddChildElem(_T("Text"),GetString());
		switch (m_eAlign)
		{
		case TextAlignmentLeft:
			str=_T("left");
			break;
		case TextAlignmentRight:
			str=_T("right");
			break;
		case TextAlignmentCenter:
			str=_T("center");
			break;
		default :
			ASSERT(FALSE);
		}
		xml.AddChildElem(_T("Align"),str);

		xml.AddChildElem(_T("Position"));
		xml.IntoElem();
			m_position.SerializeXML(ar);
		xml.OutOfElem();

		xml.AddChildElemEx(_T("Angle"),m_fAngle);

		m_font.SerializeXML(ar);

		xml.OutOfElem();
	}
	else
	{
		if  (!xml.FindChildElem(szTag))
				return;

		xml.IntoElem();

			CPGLObject::SerializeXML(ar);

			xml.ResetChildPos();
			if(xml.FindChildElem(_T("Text")))
				m_sText=xml.GetChildData();

			xml.ResetChildPos();
			if (xml.FindChildElem(_T("Align")))
			{
				str=xml.GetChildData();
				if (str == _T("left"))
					m_eAlign=TextAlignmentLeft;
				else if (str == _T("right"))
					m_eAlign=TextAlignmentRight;
				else if (str == _T("center"))
					m_eAlign=TextAlignmentCenter;
			}				

			xml.ResetChildPos();
			if (xml.FindChildElem(_T("Position")))
			{
				xml.IntoElem();
					m_position.SerializeXML(ar);
				xml.OutOfElem();
			}

			xml.FindGetChildData(_T("Angle"),m_fAngle);

			xml.ResetChildPos();
			m_font.SerializeXML(ar);

		xml.OutOfElem();
	}
}
#ifdef _DEBUG
void CPGLText::Dump( CDumpContext& dc ) const
{
    // call base class function first
    CPGLObject::Dump( dc );

    // now do the stuff for our specific class
	dc << _T("--- CPGLText ID ") << GetID() <<_T(" ---\n");
}

void CPGLText::AssertValid() const
{
    // call inherited AssertValid first
    CPGLObject::AssertValid();
} 
#endif

CPGLText::CPGLText(LPCTSTR _sText, const CPGLColor& textColor)
: CPGLObject(textColor), m_eAlign(gfxinterface::TextAlignmentLeft), m_sText(_sText)
{ 
	m_fAngle=0;

	// setting to black
	SetColor(0.0,0.0,0.0,1.0f);

	LoadBitmap(IDB_PGL_TEXT_BITMAP);
};

CPGLText& CPGLText::operator=(const CPGLText& t)
{
	if (&t!=this)
	{
		// invoke CPGLObject copy assignement operator
		this->CPGLObject::operator=(t);

		m_eAlign=t.m_eAlign;
		m_sText=t.m_sText; 
		m_fAngle=t.m_fAngle;
		m_position=t.m_position;
		m_font.SetLOGFONT( t.m_font.GetLOGFONT());

		LoadBitmap(IDB_PGL_TEXT_BITMAP);
	}
	return *this;
}

void CPGLText::GetUnionExtent(gfxinterface::CGfxInterface& gfx, CPGLRectD& rExtent)
{
   PostUpdateExtent();
	CPGLObject::GetUnionExtent(gfx,rExtent);

	GetExtent().SetUnion(rExtent);

	rExtent.xValid=false;
	rExtent.yValid=false;
}

///virtual function to override in derived classes
void CPGLText::UpdateExtent(gfxinterface::CGfxInterface& gfx)
{
	using namespace gfxinterface;

	SPointD anchor;

	// Calling base class first
	CPGLObject::UpdateExtent(gfx);

	CPGLRectD rExtent;
	LOGFONT lf=m_font.GetLOGFONT();

	gfxinterface::SPointD pf;

	pf =  gfx.MeasureString(m_sText, lf);
	pf.x=fabs(pf.x);
	pf.y=fabs(pf.y);

	m_PixelSize.x=(float)pf.x;
	m_PixelSize.y=(float)pf.y;

	anchor=m_position.GetWorldPosition( gfx);

	// updating extent...
	rExtent.left=anchor.x;
	rExtent.right=anchor.x;
	rExtent.bottom=anchor.y;
	rExtent.top=anchor.y;

	SetExtent(rExtent);
}

double CPGLText::GetPixelHeight() const
{	
   static double radtodeg=  PGL_PI/ 180.0;
	double a=m_fAngle*radtodeg;
	return m_PixelSize.x*fabs(sin(a))+m_PixelSize.y*fabs(cos(a));
};

double CPGLText::GetPixelAnchorToBottom() const
{
	return GetPixelHeight()-GetPixelAnchorToTop();
}


double CPGLText::GetPixelAnchorToTop() const
{
   using namespace gfxinterface;

	ASSERT(m_fAngle <=360);
	ASSERT(m_fAngle >= 0);

	static double radtodeg=PGL_PI/ 180.0; 
	double a= m_fAngle * radtodeg;
	double sinx=sin(a); 
	double siny=sin(a+PGL_PI/2); 

	switch(m_eAlign)
	{
	case TextAlignmentLeft:
		return __max(0,sinx)*m_PixelSize.x+__max(0,siny)*m_PixelSize.y;
	case TextAlignmentCenter:
		return fabs(sinx)*m_PixelSize.x/2+__max(0,siny)*m_PixelSize.y;
	case TextAlignmentRight:
		return fabs(__min(0,sinx))*m_PixelSize.x+__max(0,siny)*m_PixelSize.y;
	default:
		ASSERT(FALSE);
		return 0;
	}
}

double CPGLText::GetPixelWidth() const			
{
	static double radtodeg=PGL_PI/ 180.0; 
	double a= m_fAngle * radtodeg;

	return fabs(m_PixelSize.x*cos(a))+fabs(m_PixelSize.y*sin(a));
};

double CPGLText::GetPixelAnchorToLeft() const
{
	return GetPixelWidth()-GetPixelAnchorToRight();
}

double CPGLText::GetPixelAnchorToRight() const
{
   using namespace gfxinterface;

	static double radtodeg=PGL_PI/ 180.0; 
	double a= m_fAngle * radtodeg;
	double cosx=cos(a); 
	double cosy=cos(a+PGL_PI/2); 

	switch(m_eAlign)
	{
	case TextAlignmentLeft:
		return __max(0,cosx)*m_PixelSize.x+__max(0,cosy)*m_PixelSize.y;
	case TextAlignmentCenter:
		return fabs(cosx)*m_PixelSize.x/2.0+__max(0,cosy)*m_PixelSize.y;
	case TextAlignmentRight:
		return fabs(__min(0,cosx))*m_PixelSize.x+__max(0,cosy)*m_PixelSize.y;
	default:
		ASSERT(FALSE);
		return 0;
	}
}

void CPGLText::PlotGfx(gfxinterface::CGfxInterface& gfx)
{
   if (!IsVisible())
		return;

	if (!IsInLayer(gfx.GetLayer()))
		return;

	// calling base class plot
	CPGLObject::PlotGfx(gfx);

	gfx.AddComment(_T("--- CPGLText ---"));	

	// setting font...
	m_font.SetFontGfx(gfx);

	// writing down text...
	gfxinterface::SPointD anchor=m_position.GetWorldPosition(gfx);
	gfx.DrawText(anchor.x,anchor.y,(LPCTSTR)m_sText,m_eAlign,m_fAngle);
}

void CPGLText::AddPropertyPage(CPropertySheet* pPropSheet)
{
	ASSERT_VALID(pPropSheet);
	// call own functions
	CPGLTextPropPage* propPage=new CPGLTextPropPage(this);
	pPropSheet->AddPage(propPage);

	// first call base class function
	CPGLObject::AddPropertyPage(pPropSheet);
}

void CPGLText::PreparePropertyMap()
{
	using namespace std;
	static bool bLoaded(false);
	
	if (bLoaded)
		return;
	bLoaded=true;

	CPGLObject::PreparePropertyMap();

	m_mProps[_T("Text.String")]=PropertyPair(TextPropString, _T("String: the text"));
	m_mProps[_T("Text.Position")]=PropertyPair(TextPropPosition, _T("Position: text position (see CPGLCoord2D)"));
	m_mProps[_T("Text.Angle")]=PropertyPair(TextPropAngle, _T("Angle: text angle in radians"));
	m_mProps[_T("Text.Align")]=PropertyPair(TextPropAlign, _T("Align: left, right, center"));
	m_mProps[_T("Text.Font")]=PropertyPair(TextPropFont, _T("Font: Font (see CPGLFont)"));
}

bool CPGLText::SetProperty( LPCTSTR szProp, LPCTSTR szValue)
{
	using namespace gfxinterface;

	if (CPGLObject::SetProperty(szProp, szValue))
		return true;

	CString sv(szValue);
	sv.MakeLower();

	float f;
	switch( StringToTextProp( szProp ))
	{
	case TextPropString:
			SetString( szValue );
			return true;
	case TextPropPosition:
		return true;
	case TextPropAngle:
		if( _stscanf( sv, _T("%g"), &f ) )
			SetAngle(f); 
		return true;
	case TextPropAlign:
		if ( sv == "left" )
			SetAlign( TextAlignmentLeft );
		else if (sv == "center")
			SetAlign( TextAlignmentCenter);
		else if (sv == "right")
			SetAlign( TextAlignmentRight);
		return true;
	case TextPropFont:
		return true;
	default:
		return false;
	}
};

bool CPGLText::Get( LPCTSTR szProp, CString& str)
{
	using namespace gfxinterface;

	if(CPGLObject::Get(szProp, str))
		return true;

	switch( StringToTextProp( szProp ))
	{
	case TextPropString:
		str=GetString();
		return true;
	case TextPropPosition:
		return true;
	case TextPropAngle:
		str.Format(_T("%g"), GetAngle());
		return true;
	case TextPropAlign:
		switch( GetAlign() )
		{
		case TextAlignmentLeft:
			str=_T("left");
			break;
		case TextAlignmentRight:
			str=_T("right");
			break;
		case TextAlignmentCenter:
			str=_T("center");
			break;
		}
		return true;
	default:
		return false;
	};
}
